فارسی

یاد بگیرید چگونه از AbortController جاوا اسکریپت برای لغو مؤثر عملیات ناهمزمان مانند درخواست‌های fetch، تایمرها و موارد دیگر استفاده کنید و کدی تمیزتر و با عملکرد بهتر داشته باشید.

AbortController در جاوا اسکریپت: تسلط بر لغو عملیات ناهمزمان

در توسعه وب مدرن، عملیات ناهمزمان همه جا هستند. واکشی داده از APIها، تنظیم تایمرها و مدیریت تعاملات کاربر اغلب شامل کدهایی است که به طور مستقل و بالقوه برای مدت زمان طولانی اجرا می‌شوند. با این حال، سناریوهایی وجود دارد که در آن‌ها نیاز به لغو این عملیات قبل از تکمیل شدنشان دارید. اینجاست که رابط AbortController در جاوا اسکریپت به کمک می‌آید. این رابط یک روش تمیز و کارآمد برای ارسال سیگنال درخواست لغو به عملیات DOM و دیگر وظایف ناهمزمان فراهم می‌کند.

درک نیاز به لغو عملیات

قبل از پرداختن به جزئیات فنی، بیایید درک کنیم که چرا لغو عملیات ناهمزمان مهم است. این سناریوهای رایج را در نظر بگیرید:

معرفی AbortController و AbortSignal

رابط AbortController برای حل مشکل لغو عملیات ناهمزمان طراحی شده است. این رابط از دو جزء کلیدی تشکیل شده است:

استفاده پایه: لغو درخواست‌های Fetch

بیایید با یک مثال ساده از لغو یک درخواست fetch شروع کنیم:


const controller = new AbortController();
const signal = controller.signal;

fetch('https://api.example.com/data', { signal })
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log('Data:', data);
  })
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
    } else {
      console.error('Fetch error:', error);
    }
  });

// To cancel the fetch request:
controller.abort();

توضیح:

  1. ما یک نمونه AbortController ایجاد می‌کنیم.
  2. ما AbortSignal مرتبط را از controller دریافت می‌کنیم.
  3. ما signal را به گزینه‌های fetch ارسال می‌کنیم.
  4. اگر نیاز به لغو درخواست داشته باشیم، controller.abort() را فراخوانی می‌کنیم.
  5. در بلوک .catch()، ما بررسی می‌کنیم که آیا خطا از نوع AbortError است یا خیر. اگر چنین باشد، می‌دانیم که درخواست لغو شده است.

مدیریت AbortError

هنگامی که controller.abort() فراخوانی می‌شود، درخواست fetch با یک AbortError رد (reject) خواهد شد. مدیریت صحیح این خطا در کد شما بسیار مهم است. عدم انجام این کار می‌تواند منجر به رد شدن پرامیس‌های مدیریت نشده و رفتار غیرمنتظره شود.

در اینجا یک مثال قوی‌تر با مدیریت خطا آورده شده است:


const controller = new AbortController();
const signal = controller.signal;

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data', { signal });
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const data = await response.json();
    console.log('Data:', data);
    return data;
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
      return null; // Or throw the error to be handled further up
    } else {
      console.error('Fetch error:', error);
      throw error; // Re-throw the error to be handled further up
    }
  }
}

fetchData();

// To cancel the fetch request:
controller.abort();

بهترین روش‌ها برای مدیریت AbortError:

لغو تایمرها با AbortSignal

از AbortSignal همچنین می‌توان برای لغو تایمرهای ایجاد شده با setTimeout یا setInterval استفاده کرد. این کار به کمی تلاش دستی بیشتری نیاز دارد، زیرا توابع تایمر داخلی به طور مستقیم از AbortSignal پشتیبانی نمی‌کنند. شما باید یک تابع سفارشی ایجاد کنید که به سیگنال لغو گوش دهد و هنگام فعال شدن آن، تایمر را پاک کند.


function cancellableTimeout(callback, delay, signal) {
  let timeoutId;

  const timeoutPromise = new Promise((resolve, reject) => {
    timeoutId = setTimeout(() => {
      resolve(callback());
    }, delay);

    signal.addEventListener('abort', () => {
      clearTimeout(timeoutId);
      reject(new Error('Timeout Aborted'));
    });
  });

  return timeoutPromise;
}

const controller = new AbortController();
const signal = controller.signal;


cancellableTimeout(() => {
  console.log('Timeout executed');
}, 2000, signal)
.then(() => console.log("Timeout finished successfully"))
.catch(err => console.log(err));

// To cancel the timeout:
controller.abort();

توضیح:

  1. تابع cancellableTimeout یک تابع بازگشتی (callback)، یک تأخیر (delay) و یک AbortSignal را به عنوان آرگومان دریافت می‌کند.
  2. این تابع یک setTimeout را تنظیم می‌کند و شناسه تایم‌اوت را ذخیره می‌کند.
  3. این تابع یک شنونده رویداد به AbortSignal اضافه می‌کند که به رویداد abort گوش می‌دهد.
  4. هنگامی که رویداد abort فعال می‌شود، شنونده رویداد تایمر را پاک می‌کند و پرامیس را رد (reject) می‌کند.

لغو شنوندگان رویداد (Event Listeners)

مشابه تایمرها، می‌توانید از AbortSignal برای لغو شنوندگان رویداد استفاده کنید. این کار به ویژه زمانی مفید است که می‌خواهید شنوندگان رویداد مرتبط با کامپوننتی که در حال جدا شدن (unmount) است را حذف کنید.


const controller = new AbortController();
const signal = controller.signal;

const button = document.getElementById('myButton');

button.addEventListener('click', () => {
  console.log('Button clicked!');
}, { signal });

// To cancel the event listener:
controller.abort();

توضیح:

  1. ما signal را به عنوان یک گزینه به متد addEventListener ارسال می‌کنیم.
  2. هنگامی که controller.abort() فراخوانی می‌شود، شنونده رویداد به طور خودکار حذف خواهد شد.

AbortController در کامپوننت‌های React

در React، می‌توانید از AbortController برای لغو عملیات ناهمزمان هنگام جدا شدن یک کامپوننت استفاده کنید. این کار برای جلوگیری از نشت حافظه و خطاهای ناشی از به‌روزرسانی کامپوننت‌های جدا شده ضروری است. در اینجا یک مثال با استفاده از هوک useEffect آورده شده است:


import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;

    async function fetchData() {
      try {
        const response = await fetch('https://api.example.com/data', { signal });
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        const data = await response.json();
        setData(data);
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Fetch aborted');
        } else {
          console.error('Fetch error:', error);
        }
      }
    }

    fetchData();

    return () => {
      controller.abort(); // Cancel the fetch request when the component unmounts
    };
  }, []); // Empty dependency array ensures this effect runs only once on mount

  return (
    
{data ? (

Data: {JSON.stringify(data)}

) : (

Loading...

)}
); } export default MyComponent;

توضیح:

  1. ما یک AbortController را در داخل هوک useEffect ایجاد می‌کنیم.
  2. ما signal را به درخواست fetch ارسال می‌کنیم.
  3. ما یک تابع پاک‌سازی از هوک useEffect بازمی‌گردانیم. این تابع هنگام جدا شدن کامپوننت فراخوانی خواهد شد.
  4. در داخل تابع پاک‌سازی، ما controller.abort() را برای لغو درخواست fetch فراخوانی می‌کنیم.

موارد استفاده پیشرفته

زنجیره‌سازی AbortSignalها

گاهی اوقات، ممکن است بخواهید چندین AbortSignal را به هم زنجیر کنید. به عنوان مثال، ممکن است یک کامپوننت والد داشته باشید که نیاز به لغو عملیات در کامپوننت‌های فرزند خود دارد. شما می‌توانید با ایجاد یک AbortController جدید و ارسال سیگنال آن به هر دو کامپوننت والد و فرزند به این هدف دست یابید.

استفاده از AbortController با کتابخانه‌های شخص ثالث

اگر از کتابخانه شخص ثالثی استفاده می‌کنید که به طور مستقیم از AbortSignal پشتیبانی نمی‌کند، ممکن است نیاز داشته باشید کد خود را با مکانیزم لغو آن کتابخانه تطبیق دهید. این کار ممکن است شامل پیچیدن توابع ناهمزمان کتابخانه در توابع خودتان باشد که AbortSignal را مدیریت می‌کنند.

مزایای استفاده از AbortController

سازگاری با مرورگرها

AbortController به طور گسترده در مرورگرهای مدرن از جمله Chrome، Firefox، Safari و Edge پشتیبانی می‌شود. می‌توانید برای آخرین اطلاعات، جدول سازگاری را در MDN Web Docs بررسی کنید.

پلی‌فیل‌ها (Polyfills)

برای مرورگرهای قدیمی‌تری که به طور بومی از AbortController پشتیبانی نمی‌کنند، می‌توانید از یک پلی‌فیل استفاده کنید. پلی‌فیل قطعه کدی است که عملکرد یک ویژگی جدیدتر را در مرورگرهای قدیمی‌تر فراهم می‌کند. چندین پلی‌فیل AbortController به صورت آنلاین در دسترس هستند.

نتیجه‌گیری

رابط AbortController ابزاری قدرتمند برای مدیریت عملیات ناهمزمان در جاوا اسکریپت است. با استفاده از AbortController، می‌توانید کدی تمیزتر، با عملکرد بهتر و قوی‌تر بنویسید که لغو عملیات را به خوبی مدیریت می‌کند. چه در حال واکشی داده از APIها باشید، چه در حال تنظیم تایمرها یا مدیریت شنوندگان رویداد، AbortController می‌تواند به شما در بهبود کیفیت کلی برنامه‌های وب خود کمک کند.

مطالعه بیشتر